﻿using System;
using System.IO;
using System.Text;
using System.Xml;

namespace Com.OpenCode.Framework.Core
{
    /// <summary>
    ///     插件对象定义。
    /// </summary>
    public class Addin
    {
        public Addin()
        {
            StartLevel = 100;
        }

        /// <summary>
        ///     插件激活器。
        /// </summary>
        private IAddinActivator Activator;

        /// <summary>
        ///     Doozer解析器集合。
        /// </summary>
        public virtual Doozers Doozers { get; private set; }

        /// <summary>
        ///     插件树运行时环境。
        /// </summary>
        public virtual Runtime Runtime { get; private set; }

        /// <summary>
        ///     插件启动顺序。
        /// </summary>
        public virtual int StartLevel { get; private set; }

        public virtual bool Baned { get; private set; }

        /// <summary>
        ///     插件文件中包含的插件跟节点。
        /// </summary>
        public virtual Plugin Plugin { get; private set; }

        /// <summary>
        ///     插件的名称。
        /// </summary>
        public virtual string Name {
            get
            {
                if (FileInfo!=null)
                {
                    return FileInfo.Name;
                }
                return "";
            }
        }

        /// <summary>
        /// 当前插件文件信息。
        /// </summary>
        public virtual FileInfo FileInfo { get; private set; }

        public virtual void InitCore()
        {
            if (Plugin == null)
            {
                throw new Exception("初始化之前请先加载插件文件，或加载的插件文件为空。");
            }
            Runtime = new Runtime();
            Doozers = new Doozers();
            Plugin _RuntimePlugin = Plugin.Search("Runtime");
            if (_RuntimePlugin != null)
            {
                foreach (var item in _RuntimePlugin.Children)
                {
                    string fileName = item.Properties.Get("Assembly").ToString();
                    if (string.IsNullOrEmpty(fileName))
                    {
                        throw new Exception("Runtime运行时配置为空。");
                    }
                    if (!fileName.EndsWith(".dll"))
                    {
                        throw new Exception(string.Format("需要导入的类库{0}文件类型有误，类型应为.dll。", fileName));
                    }
                    string filePath = Path.Combine(FileInfo.DirectoryName, fileName);
                    if (!File.Exists(filePath))
                    {
                        throw new Exception(string.Format("未找到名称为{0}的类库。", fileName));
                    }
                    Runtime.Add(filePath);
                }
            }
            Plugin _DoozerPlugin = Plugin.Search("Doozer");
            if (_DoozerPlugin != null)
            {
                foreach (var item in _DoozerPlugin.Children)
                {
                    string classPath = item.Properties.Get("Class").ToString();
                    if (string.IsNullOrEmpty(classPath))
                    {
                        throw new Exception(string.Format("注入Doozer[{0}]的名称不能为空。", item.Name));
                    }
                    IDoozer doozer = Runtime.CreateObject(classPath) as IDoozer;
                    Doozers.Add(doozer);
                }
            }
            if (Plugin.Properties.Exist("StartLevel"))
            {
                StartLevel = Plugin.Properties.GetInt("StartLevel");
            }
            if (Plugin.Properties.Exist("Activator"))
            {
                string activator = Plugin.Properties.GetString("Activator");
                if (!string.IsNullOrEmpty(activator))
                {
                    Activator = Runtime.CreateObject(activator) as IAddinActivator;
                }
            }
        }

        /// <summary>
        ///     启动插件。
        /// </summary>
        public virtual void Start()
        {
            if (Activator != null && !Baned)
            {
                Activator.Start(this);
            }
        }

        /// <summary>
        ///     停止插件。
        /// </summary>
        public virtual void Stop()
        {
            if (Activator != null)
            {
                Activator.Stop(this);
            }
        }

        /// <summary>
        ///  禁用该插件。
        /// </summary>
        public virtual void Ban()
        {
            Baned = true;
        }

        public virtual object Bulid(BuildItemEventArgs args)
        {
            if (Doozers.Contain(args.Plugin.Name))
            {
                object obj = Doozers.Get(args.Plugin.Name).BuildItem(args);
                args.Plugin.Owner = obj;
                return obj;
            }
            throw new Exception(string.Format("未找到名称为{0}的解析器。", args.Plugin.Name));
        }

        public virtual object Bulid(Plugin plugin, object owner=null)
        {
            BuildItemEventArgs e = new BuildItemEventArgs
            {
                Plugin = plugin,
                Owner = owner
            };
            return Bulid(e);
        }

        public virtual object Bulid(string name, object owner=null)
        {
            BuildItemEventArgs e = new BuildItemEventArgs
            {
                Plugin = Plugin.Search(name),
                Owner = owner
            };
            return Bulid(e);
        }

        public virtual object Bulid(string name, string key, string value, object owner=null)
        {
            BuildItemEventArgs e = new BuildItemEventArgs
            {
                Plugin = Plugin.Search(name, key, value),
                Owner = owner
            };
            return Bulid(e);
        }

        /// <summary>
        ///     继承一个插件的运行时环境跟Doozer解析器。
        /// </summary>
        /// <param name="addin">需要继承的插件。</param>
        public virtual void Genetic(Addin addin)
        {
            for (int i = 0; i < addin.Runtime.Count; i++)
            {
                Runtime.Add(addin.Runtime.Get(i));
            }
            for (int i = 0; i < addin.Doozers.Count; i++)
            {
                Doozers.Add(addin.Doozers.Get(i));
            }
        }

        /// <summary>
        ///     装载插件。
        /// </summary>
        public virtual void Load(string path)
        {
            try
            {
                FileStream pFileStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                StreamReader _Read = new StreamReader(pFileStream, Encoding.UTF8);
                LoadFromString(_Read.ReadToEnd());
                _Read.Close();
                pFileStream.Close();
                FileInfo = new FileInfo(path);
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("读取插件文件[{0}]出现错误{1}", path, ex.Message));
            }
        }

        public virtual void Save(string path)
        {
            XmlDocument _Document = new XmlDocument();
            Save(_Document, _Document, Plugin);
            _Document.Save(path);
        }

        public virtual void Save()
        {
            XmlDocument _Document = new XmlDocument();
            Save(_Document, _Document, Plugin);
            _Document.Save(FileInfo.FullName);
        }

        /// <summary>
        ///     保存文字符串。
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            XmlDocument _Document = new XmlDocument();
            Save(_Document, _Document, Plugin);
            MemoryStream stream = new MemoryStream();
            XmlTextWriter writer = new XmlTextWriter(stream, null) {Formatting = Formatting.Indented};
            try
            {
                _Document.Save(writer);
                StreamReader streamReader = new StreamReader(stream, Encoding.UTF8);
                stream.Position = 0;
                string returnValue = streamReader.ReadToEnd();
                return returnValue;
            }
            finally
            {
                stream.Close();
            }
        }

        #region 内部方法

        /// <summary>
        ///     在xml节点中寻找插件配置。
        /// </summary>
        /// <param name="node"></param>
        private Plugin FindPlugin(XmlNode node)
        {
            Plugin _Plugin = null;
            if (node.NodeType == XmlNodeType.Element)
            {
                _Plugin = new Plugin(this)
                {
                    Name = node.Name
                };
                if (node.Attributes != null && (node.Attributes.Count != 0))
                {
                    for (int i = 0; i < node.Attributes.Count; i++)
                    {
                        XmlAttribute attribute = node.Attributes[i];
                        _Plugin.Properties.Add(attribute.Name, attribute.InnerText);
                    }
                }
                if (node.HasChildNodes)
                {
                    for (int i = 0; i < node.ChildNodes.Count; i++)
                    {
                        Core.Plugin findPlugin = FindPlugin(node.ChildNodes[i]);
                        if (findPlugin != null)
                        {
                            _Plugin.Children.Add(findPlugin);
                        }
                    }
                }
            }
            return _Plugin;
        }

        private void Save(XmlDocument document, XmlNode node, Plugin plugin)
        {
            XmlNode _XmlNode = document.CreateElement(plugin.Name);
            node.AppendChild(_XmlNode);
            foreach (var item in plugin.Properties.Dictionary)
            {
                (_XmlNode as XmlElement).SetAttribute(item.Key.ToString(), item.Value.ToString());
            }
            foreach (var item in plugin.Children)
            {
                Save(document, _XmlNode, item);
            }
        }

        /// <summary>
        ///     根据文件流加载插件。
        /// </summary>
        /// <param name="fileStream"></param>
        private void LoadFromString(string fileStream)
        {
            XmlDocument xml = new XmlDocument();
            try
            {
                xml.LoadXml(fileStream);
            }
            catch (Exception)
            {
                throw new Exception(string.Format("读取插件文件[{0}]出现错误", fileStream));
            }
            Plugin = FindPlugin(xml.DocumentElement);
        }

        #endregion
    }
}